#!/usr/bin/env python
# -*- coding: utf-8; -*-
import json
import requests

from django.shortcuts import render
from django.http import JsonResponse, HttpResponse
from django.forms import model_to_dict
from django.views.decorators.csrf import csrf_exempt
from django.urls import reverse

from web.forms.file import FolderModelForm
from web import models
from web.forms.file import FolderModelForm, FileModelForm

from utils.tencent.cos import delete_file, delete_file_list, credential

# file.file 试图函数处理的 url 请求下面两种格式
# http://127.0.0.1:8002/manage/1/file/            获取或创建根目录文件夹
# http://127.0.0.1:8002/manage/1/file/?folder=1   获取或创建非根目录文件夹
# 依照这两种格式的差异(类似项目wiki列表页面的设定)，编写 file 视图


def file(request, project_id):
    """ 文件列表 & 添加文件夹 """

    # 判读是否创建根目录的新文件夹
    parent_object = None
    # 是否限制地址栏的输入参数名称呢？
    folder_id = request.GET.get('folder', "")  # 假如在地址栏填了错误的参数
    # 名 ?folder_id=1 会按照
    # folder_id = "" 处理(即返回
    # 根目录文件夹列表页面)，后续
    # 创建的目录没有父目录
    if folder_id.isdecimal():
        # 对folder_id约束为数字
        parent_object = models.FileRepository.objects.filter(
            id=int(folder_id), file_type=2,
            project=request.tracer.project).first()

    # 对于第二种URL格式
    if request.method == "GET":
        breadcrumb_list = []
        parent = parent_object
        while parent:
            # breadcrumb_list.insert(0, {'id': parent.id, 'name': parent.name})
            breadcrumb_list.insert(0, model_to_dict(parent, ['id', 'name']))
            parent = parent.parent

        # 当前目录下所有的文件 & 文件夹获取到即可
        queryset = models.FileRepository.objects.filter(
            project=request.tracer.project)
        if parent_object:
            # 进入了某目录
            file_object_list = queryset.filter(
                parent=parent_object).order_by('-file_type')
        else:
            # 根目录
            file_object_list = queryset.filter(
                parent__isnull=True).order_by('-file_type')

        form = FolderModelForm(request, parent_object)
        context = {
            'form': form,
            "file_object_list": file_object_list,
            "breadcrumb_list": breadcrumb_list,
            'folder_object': parent_object
        }

        return render(request, 'web/file.html', context)

    # POST 添加文件夹 & 文件夹的修改
    # 前端页面的表单中，由一个id为 fid 的input元素，决定是添加文件夹或编辑文件夹，这样可以同一各按钮
    fid = request.POST.get('fid', '')

    edit_object = None
    if fid.isdecimal():
        edit_object = models.FileRepository.objects.filter(
            id=int(fid), file_type=2, project=request.tracer.project).first()

    if edit_object:
        form = FolderModelForm(request,
                               parent_object,
                               data=request.POST,
                               instance=edit_object)
    else:
        form = FolderModelForm(request, parent_object, data=request.POST)

    form = FolderModelForm(request, parent_object, data=request.POST)
    if form.is_valid():
        form.instance.project = request.tracer.project
        form.instance.file_type = 2
        form.instance.update_user = request.tracer.user
        form.instance.parent = parent_object
        form.save()
        return JsonResponse({'status': True})

    return JsonResponse({'status': False, 'error': form.errors})


# http://127.0.0.1:8002/manage/1/file/delete/?fid=1
def file_delete(request, project_id):
    """ 删除文件 """
    fid = request.GET.get('fid')

    # 删除数据库中的 文件 & 文件夹 （级联删除）
    delete_object = models.FileRepository.objects.filter(
        id=fid, project=request.tracer.project).first()

    # 若前端页面显示有误，使重载入页面
    if not delete_object:
        return JsonResponse({'status': False})

    if delete_object.file_type == 1:
        # 数据库文件删除、cos文件删除、项目已使用空间容量还回去

        # 删除文件，将容量还给当前项目的已使用空间
        request.tracer.project.use_space -= delete_object.file_size
        request.tracer.project.save()

        # cos中删除文件
        delete_file(request.tracer.project.bucket,
                    request.tracer.project.region, delete_object.key)

        # 在数据库中删除当前文件
        delete_object.delete()

        return JsonResponse({'status': True})

    # 删除文件夹（找到文件夹下所有的文件->数据库文件删除、cos文件删除、项目已使用空间容量还回去）
    # delete_object
    # 找他下面的 文件和文件夹

    # models.FileRepository.objects.filter(parent=delete_object) # 文件 删
    # 除；文件夹 继续向里差

    # 两个列表：文件夹中的文件夹列表、文件列表
    key_list = []  # cos 支持批量删除，不必我们在后台一条条的删除，只要传
    # 过去固定格式的字典列表即可
    total_size = 0  # 文件大小(占用空间)

    folder_list = [
        delete_object,  # 这一步这里为文件夹
    ]
    for folder in folder_list:
        # 查询文件夹和文件(并文件夹排列在前)
        child_list = models.FileRepository.objects.filter(
            project=request.tracer.project,
            parent=folder).order_by('-file_type')
        for child in child_list:
            if child.file_type == 2:
                folder_list.append(child)
            else:
                # 文件大小汇总
                total_size += child.file_size
                # 加入删除文件列表，稍后调用cos批量删除函数即可
                key_list.append({"Key": child.key})

    # cos 批量删除文件
    if key_list:
        delete_file_list(request.tracer.project.bucket,
                         request.tracer.project.region, key_list)

    # 归还容量
    if total_size:
        request.tracer.project.use_space -= total_size
        request.tracer.project.save()

    # 数据库删除
    delete_object.delete()
    return JsonResponse({'status': True})


# 前端的cos对象，每次上传文件时向后端申请临时凭证，django 向 cos 申请临时凭
# 证，给前端，前端使用 js SDK 来上传文件。在 cos.py 文件中，有设置过期时间。
# 而前端的js可以自动检测是否过期，过期时会向django发送请求，自动检测授权并
# 申请
@csrf_exempt
def cos_credential(request, project_id):
    """ 获取cos上传临时凭证 """
    per_file_limit = request.tracer.package.per_file_size * 1024 * 1024
    total_file_limit = request.tracer.package.project_space * 1024 * 1024 * 1024

    total_size = 0
    # 前端上传多个文件时，传递给后端的data中包含选定文件的名字和大小(字典格
    # 式)，django无法在request.GET 或 request.POST 中直接获取这种格式下的数据
    # 解决办法：ajax请求中使用 JSON.tringify 对数据json格式化，相应的django中也要
    # 从 request.body 中获取该json数据
    file_list = json.loads(request.body.decode('utf-8'))
    for item in file_list:
        # 文件的字节大小 item['size'] = B
        # 单文件限制的大小 M
        # 超出限制
        if item['size'] > per_file_limit:
            msg = "单文件超出限制（最大{}M），文件：{}，请升级套餐。".format(
                request.tracer.package.per_file_size, item['name'])
            return JsonResponse({'status': False, 'error': msg})
        total_size += item['size']

        # 做容量限制：单文件 & 总容量

    # 总容量进行限制
    # request.tracer.package.project_space  # 项目的允许的空间
    # request.tracer.project.use_space # 项目已使用的空间
    if request.tracer.project.use_space + total_size > total_file_limit:
        return JsonResponse({'status': False, 'error': "容量超过限制，请升级套餐。"})

    data_dict = credential(request.tracer.project.bucket,
                           request.tracer.project.region)
    return JsonResponse({'status': True, 'data': data_dict})


@csrf_exempt
def file_post(request, project_id):
    """ 已上传成功的文件写入到数据 """
    """
    name: fileName,
    key: key,
    file_size: fileSize,
    parent: CURRENT_FOLDER_ID,
    # etag: data.ETag,
    file_path: data.Location
    """

    # 根据key再去cos获取文件Etag和"db7c0d83e50474f934fd4ddf059406e5"

    print(request.POST)
    # 把获取到的数据写入数据库即可
    form = FileModelForm(request, data=request.POST)
    if form.is_valid():

        # 通过ModelForm.save存储到数据库中的数据返回的isntance对象，无法通过get_xx_display获取choice的中文
        # form.instance.file_type = 1
        # form.update_user = request.tracer.user
        # instance = form.save() # 添加成功之后，获取到新添加的那个对象（instance.id,instance.name,instance.file_type,instace.get_file_type_display()

        # 校验通过：数据写入到数据库
        data_dict = form.cleaned_data
        data_dict.pop('etag')
        data_dict.update({
            'project': request.tracer.project,
            'file_type': 1,
            'update_user': request.tracer.user
        })
        instance = models.FileRepository.objects.create(**data_dict)

        # 项目的已使用空间：更新 (data_dict['file_size'])
        request.tracer.project.use_space += data_dict['file_size']
        request.tracer.project.save()

        result = {
            'id':
            instance.id,
            'name':
            instance.name,
            'file_size':
            instance.file_size,
            'username':
            instance.update_user.name,
            'datetime':
            instance.update_datetime.strftime("%Y年%m月%d日 %H:%M"),
            # 'file_type': instance.get_file_type_display()
            'download_url':
            reverse('file_download',
                    kwargs={
                        "project_id": project_id,
                        'file_id': instance.id
                    })
        }
        return JsonResponse({'status': True, 'data': result})

    return JsonResponse({'status': False, 'data': "文件错误"})


def file_download(request, project_id, file_id):
    """ 下载文件 """
    file_object = models.FileRepository.objects.filter(
        id=file_id, project_id=project_id).first()

    res = requests.get(file_object.file_path)

    # 文件分块处理(适用于大文件)
    # data = res.iter_content
    data = res.content

    # 添加响应头，用于浏览器显示下载框
    response = HttpResponse(data, content_type="application/octet-stream")

    # 设置响应头，支持中文文件名转义
    from django.utils.encoding import escape_uri_path
    response['Content-Disposition'] = "attachment; filename={}".format(
        escape_uri_path(file_object.name))

    return response
